﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
#if NET40
using System.Windows.Threading;
#endif
using HandyControl.Data;
using HandyControl.Interactivity;

namespace HandyControl.Controls;

[TemplatePart(Name = AutoCompletePanel, Type = typeof(Panel))]
[TemplatePart(Name = EditableTextBox, Type = typeof(System.Windows.Controls.TextBox))]
[TemplatePart(Name = AutoPopupAutoComplete, Type = typeof(Popup))]
public class ComboBox : System.Windows.Controls.ComboBox
{
#if NET40

    private string _searchText;

    private DispatcherTimer _autoCompleteTimer;

#endif
    private bool _isAutoCompleteAction = true;

    private Panel _autoCompletePanel;

    private System.Windows.Controls.TextBox _editableTextBox;

    private Popup _autoPopupAutoComplete;

    private const string AutoCompletePanel = "PART_AutoCompletePanel";

    private const string AutoPopupAutoComplete = "PART_Popup_AutoComplete";

    private const string EditableTextBox = "PART_EditableTextBox";

    public ComboBox()
    {
        CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
        {
            SetCurrentValue(SelectedValueProperty, null);
            SetCurrentValue(SelectedItemProperty, null);
            SetCurrentValue(SelectedIndexProperty, -1);
            SetCurrentValue(TextProperty, "");
        }));
    }

    public override void OnApplyTemplate()
    {
        if (_editableTextBox != null)
        {
            BindingOperations.ClearBinding(_editableTextBox, System.Windows.Controls.TextBox.TextProperty);
            _editableTextBox.GotFocus -= EditableTextBox_GotFocus;
            _editableTextBox.LostFocus -= EditableTextBox_LostFocus;
        }

        base.OnApplyTemplate();

        if (IsEditable)
        {
            _editableTextBox = GetTemplateChild(EditableTextBox) as System.Windows.Controls.TextBox;

            if (_editableTextBox != null)
            {
                _editableTextBox.SetBinding(SelectionBrushProperty, new Binding(SelectionBrushProperty.Name) { Source = this });
#if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472)
                _editableTextBox.SetBinding(SelectionTextBrushProperty, new Binding(SelectionTextBrushProperty.Name) { Source = this });
#endif
                _editableTextBox.SetBinding(SelectionOpacityProperty, new Binding(SelectionOpacityProperty.Name) { Source = this });
                _editableTextBox.SetBinding(CaretBrushProperty, new Binding(CaretBrushProperty.Name) { Source = this });

                if (AutoComplete)
                {
#if NET40
                    _autoCompleteTimer = new DispatcherTimer
                    {
                        Interval = TimeSpan.FromMilliseconds(500)
                    };
                    _autoCompleteTimer.Tick += AutoCompleteTimer_Tick;
#endif
                    _autoPopupAutoComplete = GetTemplateChild(AutoPopupAutoComplete) as Popup;
                    _autoCompletePanel = GetTemplateChild(AutoCompletePanel) as Panel;
                    _editableTextBox.SetBinding(System.Windows.Controls.TextBox.TextProperty, new Binding(SearchTextProperty.Name)
                    {
                        UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
                        Mode = BindingMode.OneWayToSource,
#if !NET40
                        Delay = 500,
#endif
                        Source = this
                    });
                    _editableTextBox.GotFocus += EditableTextBox_GotFocus;
                    _editableTextBox.LostFocus += EditableTextBox_LostFocus;
                }
            }
        }
    }

#if NET40

    private void AutoCompleteTimer_Tick(object sender, EventArgs e)
    {
        UpdateSearchItems(_searchText);
        _autoCompleteTimer.Stop();
    }

#endif

    private void EditableTextBox_LostFocus(object sender, RoutedEventArgs e)
    {
        if (_autoPopupAutoComplete != null)
        {
            _autoPopupAutoComplete.IsOpen = false;
        }
    }

#if !NET40
    protected override void OnDropDownClosed(EventArgs e)
    {
        base.OnDropDownClosed(e);

        _isAutoCompleteAction = false;
    }
#endif

    private void EditableTextBox_GotFocus(object sender, RoutedEventArgs e)
    {
        if (_autoPopupAutoComplete != null && _editableTextBox != null &&
            !string.IsNullOrEmpty(_editableTextBox.Text))
        {
            _autoPopupAutoComplete.IsOpen = true;
        }
    }

    protected override void OnSelectionChanged(SelectionChangedEventArgs e)
    {
        _isAutoCompleteAction = false;
        base.OnSelectionChanged(e);
#if NET40
        _isAutoCompleteAction = true;
#endif
    }

    /// <summary>
    ///     数据搜索委托
    /// </summary>
    public Func<ItemCollection, object, IEnumerable<object>> SearchFunc { get; set; }

    /// <summary>
    ///     是否自动完成输入
    /// </summary>
    public static readonly DependencyProperty AutoCompleteProperty = DependencyProperty.Register(
        nameof(AutoComplete), typeof(bool), typeof(ComboBox), new PropertyMetadata(ValueBoxes.FalseBox, OnAutoCompleteChanged));

    private static void OnAutoCompleteChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var ctl = (ComboBox) d;
        if (ctl._editableTextBox != null)
        {
            ctl.UpdateSearchItems(ctl._editableTextBox.Text);
        }
    }

    /// <summary>
    ///     是否自动完成输入
    /// </summary>
    public bool AutoComplete
    {
        get => (bool) GetValue(AutoCompleteProperty);
        set => SetValue(AutoCompleteProperty, ValueBoxes.BooleanBox(value));
    }

    /// <summary>
    ///     搜索文本
    /// </summary>
    internal static readonly DependencyProperty SearchTextProperty = DependencyProperty.Register(
        nameof(SearchText), typeof(string), typeof(ComboBox), new PropertyMetadata(default(string), OnSearchTextChanged));

    private static void OnSearchTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var ctl = (ComboBox) d;
        if (ctl._isAutoCompleteAction)
        {
#if NET40
            ctl._searchText = e.NewValue as string;
            ctl._autoCompleteTimer.Stop();
            ctl._autoCompleteTimer.Start();
#else
            ctl.UpdateSearchItems(e.NewValue as string);
#endif
        }

        ctl._isAutoCompleteAction = true;
    }

    /// <summary>
    ///     搜索文本
    /// </summary>
    internal string SearchText
    {
        get => (string) GetValue(SearchTextProperty);
        set => SetValue(SearchTextProperty, value);
    }

    public static readonly DependencyProperty SelectionBrushProperty =
        TextBoxBase.SelectionBrushProperty.AddOwner(typeof(ComboBox));

    public Brush SelectionBrush
    {
        get => (Brush) GetValue(SelectionBrushProperty);
        set => SetValue(SelectionBrushProperty, value);
    }

#if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472)

    public static readonly DependencyProperty SelectionTextBrushProperty =
        TextBoxBase.SelectionTextBrushProperty.AddOwner(typeof(ComboBox));

    public Brush SelectionTextBrush
    {
        get => (Brush) GetValue(SelectionTextBrushProperty);
        set => SetValue(SelectionTextBrushProperty, value);
    }

#endif

    public static readonly DependencyProperty SelectionOpacityProperty =
        TextBoxBase.SelectionOpacityProperty.AddOwner(typeof(ComboBox));

    public double SelectionOpacity
    {
        get => (double) GetValue(SelectionOpacityProperty);
        set => SetValue(SelectionOpacityProperty, value);
    }

    public static readonly DependencyProperty CaretBrushProperty =
        TextBoxBase.CaretBrushProperty.AddOwner(typeof(ComboBox));

    public Brush CaretBrush
    {
        get => (Brush) GetValue(CaretBrushProperty);
        set => SetValue(CaretBrushProperty, value);
    }

    /// <summary>
    ///     更新搜索的项目
    /// </summary>
    /// <param name="key"></param>
    private void UpdateSearchItems(string key)
    {
        if (_editableTextBox != null && _autoPopupAutoComplete != null)
        {
            _autoPopupAutoComplete.IsOpen = !string.IsNullOrEmpty(key);
            _autoCompletePanel.Children.Clear();

            if (SearchFunc == null)
            {
                if (!string.IsNullOrEmpty(key))
                {
                    foreach (var item in Items)
                    {
                        var content = item?.ToString();
                        if (content == null) continue;
                        if (!content.Contains(key)) continue;

                        _autoCompletePanel.Children.Add(CreateSearchItem(item));
                    }
                }
            }
            else
            {
                foreach (var item in SearchFunc.Invoke(Items, key))
                {
                    _autoCompletePanel.Children.Add(CreateSearchItem(item));
                }
            }
        }
    }

    private ComboBoxItem CreateSearchItem(object content)
    {
        var item = new ComboBoxItem
        {
            Content = content,
            Style = ItemContainerStyle,
            ContentTemplate = ItemTemplate
        };

        item.PreviewMouseLeftButtonDown += AutoCompleteItem_PreviewMouseLeftButtonDown;

        return item;
    }

    private void AutoCompleteItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        if (sender is ComboBoxItem comboBoxItem)
        {
            if (_autoPopupAutoComplete != null)
            {
                _autoPopupAutoComplete.IsOpen = false;
            }

            _isAutoCompleteAction = false;
            SelectedValue = comboBoxItem.Content;
        }
    }
}
